/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "napi_handler.h"
#include "ohos_log.h"
#include <codecvt>
#include <cstdio>
#include <locale>
#include <memory>
#include <string>


const int32_t MAX_STR_LENGTH = 2048;

NapiHandler::NapiHandler(napi_env &env, napi_callback_info &info, int argn) : env_(env), argc_(argn) {
    argv_ = new napi_value[argn];
    NAPI_CALL_RETURN_VOID(env_, napi_get_cb_info(env, info, &argc_, argv_, &thisArg_, &data_));
    NAPI_ASSERT_RETURN_VOID(env_, argc_ >= argn, "Wrong number of arguments");
}

NapiHandler::~NapiHandler() {
    if (argv_) {
        delete[] argv_;
    }
}

napi_status NapiHandler::ParseArgs(int index, const std::function<napi_status(const napi_value &)> &doArgs) {
    napi_status status;
    NAPI_ASSERT_BASE(env_, index < argc_, "Index out of range", napi_invalid_arg);
    NAPI_CALL_HANDLE(env_, status = doArgs(argv_[index]), status);
    return napi_ok;
}

napi_value NapiHandler::GetVoidValue() {
    napi_value val;
    NAPI_CALL(env_, napi_get_undefined(env_, &val));
    return val;
}

napi_value NapiHandler::CreateArray(int len, const std::function<void(int, napi_value &)> &doArray) {
    napi_value result = nullptr;
    NAPI_CALL(env_, napi_create_array_with_length(env_, len, &result));
    for (int i = 0; i < len; ++i) {
        napi_value obj;
        NAPI_CALL(env_, napi_create_object(env_, &obj));
        doArray(i, obj);
        NAPI_CALL(env_, napi_set_element(env_, result, i, obj));
    }
    return result;
}

template <> int NapiHandler::ParseArg<int>(const napi_value &arg) {
    int result = -1;
    NAPI_CALL_HANDLE(env_, napi_get_value_int32(env_, arg, &result), result);
    return result;
}

template <> uint64_t NapiHandler::ParseArg<uint64_t>(const napi_value &arg) {
    uint64_t result = -1;
    bool lose = false;
    NAPI_CALL_HANDLE(env_, napi_get_value_bigint_uint64(env_, arg, &result, &lose), result);
    return result;
}

template <> double NapiHandler::ParseArg<double>(const napi_value &arg) {
    double result = 0;
    NAPI_CALL_HANDLE(env_, napi_get_value_double(env_, arg, &result), result);
    return result;
}

template <> bool NapiHandler::ParseArg<bool>(const napi_value &arg) {
    bool result = false;
    NAPI_CALL_HANDLE(env_, napi_get_value_bool(env_, arg, &result), result);
    return result;
}

template <> std::string NapiHandler::ParseArg<std::string>(const napi_value &arg) {
    std::string result = "";
    size_t length = 0;

    NAPI_CALL_HANDLE(env_, napi_get_value_string_utf8(env_, arg, nullptr, 0, &length), result);
    if (length == 0) {
        return result;
    }
    if (length < 0 || length > MAX_STR_LENGTH) {
        LOGE("%s string too long malloc failed, length: %d", __func__, length);
        return result;
    }

    std::unique_ptr<char[]> buf = std::make_unique<char[]>(length + 1);
    if (buf.get() == nullptr) {
        LOGE("%s nullptr js object to string malloc failed", __func__);
        return result;
    }

    (void)memset(buf.get(), 0, length + 1);
    NAPI_CALL_HANDLE(env_, napi_get_value_string_utf8(env_, arg, buf.get(), length + 1, &length), result);
    result = buf.get();
    return result;
}

template <> napi_ref NapiHandler::ParseArg<napi_ref>(const napi_value &arg) {
    napi_ref result = nullptr;
    NAPI_CALL_HANDLE(env_, napi_create_reference(env_, arg, 1, &result), result);
    return result;
}

template <> napi_value NapiHandler::GetNapiValue<bool>(const bool &v) {
    napi_value val;
    NAPI_CALL(env_, napi_get_boolean(env_, v, &val));
    return val;
}

template <> napi_value NapiHandler::GetNapiValue<int32_t>(const int32_t &v) {
    napi_value val;
    NAPI_CALL(env_, napi_create_int32(env_, v, &val));
    return val;
}

template <> napi_value NapiHandler::GetNapiValue<std::string>(const std::string &v) {
    napi_value val;
    NAPI_CALL(env_, napi_create_string_utf8(env_, v.c_str(), v.length(), &val));
    return val;
}
